perm filename NOTES.C[1,RWF] blob sn#548612 filedate 1980-12-05 generic text, type C, neo UTF8
COMMENT āŠ—   VALID 00019 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00004 00002	This is file FLOYD.C[1,PHY] at the Stanford AI Lab (SU-AI), last written
C00006 00003		PROCEDURE NAME(...) BEGIN
C00008 00004
C00010 00005	APPENDIX J.  FILE SPECIFICATIONS [D4].
C00013 00006	HOW TO TALK ABOUT SETS OF FILES:
C00017 00007	APPENDIX K.  COMMON PROGRAMMING ERRORS.
C00023 00008	(21)	Conditional expressions without alternative:  A := IF B THEN C+D .
C00024 00009	Examples follow, showing typical error messages resulting from common SAIL
C00026 00010	We shall show the effects of making changes that leave the program
C00029 00011	The first error causes multiple error messages, all at places where COL is
C00033 00012		BLOCK FOUND WHILE FLUSHING STATEMENT - WILL TRY TO PARSE IT.
C00036 00013	The second message correctly diagnosed the problem.  The compiler soon got
C00039 00014	APPENDIX M.  POLYNOMIAL MULTIPLICATION.
C00045 00015		BEGIN "MAIN"
C00047 00016	[Into SAIL notes, insert before CONVERSION OF STRINGS TO NUMBERS.]
C00049 00017	(4)	To remove initial, final, and multiple spaces from a string  S :
C00051 00018	RANDOM NUMBERS.
C00063 00019	For a normally distributed number (this distribution is the bell-shaped
C00067 ENDMK
CāŠ—;
This is file FLOYD.C[1,PHY] at the Stanford AI Lab (SU-AI), last written
July 31, 1978.  Bob Floyd (aka RWF at SAIL, R.RWF at LOTS) wrote it, maintains
it, and welcomes suggestions.

APPENDIX I.  A DISCIPLINE OF PUNCTUATION AND INDENTATION FOR SAIL COMMANDS.
 
If you have trouble remembering where the semicolons go, use this method, which
uses more blocks than are absolutely necessary, but which is easy to read and 
to write correctly.

All primitive commands are written with a semicolon at the end of the line.

All composite commands are written in one of these forms:
 
	FOR ... DO BEGIN
		C1
		C2
		.
		.
		.
		Cn
	END;
 
	WHILE ... DO BEGIN
		C1
		C2
		.
		.
		.
		Cn
	END;
 
	IF ... THEN BEGIN
		C1
		C2
		.
		.
		.
		Cn
	END;

	IF ... THEN BEGIN
		C1
		C2
		.
		.
		.
		Cn
	END ELSE BEGIN
		Cn+1
		Cn+2
		.
		.
		.
		Cm
	END;
	PROCEDURE NAME(...); BEGIN
		C1
		C2
		.
		.
		.
		Cn
	END;
 
	CASE ... OF BEGIN
  	        [1] C1
		[2] C2
		[3] C3
		.
		.
		.
		[n] Cn          (it is essential not to end this line
	END;                     with a semicolon)
  
        DO BEGIN	  
		C1
		C2
		.
		.
		.
		Cn
	END UNTIL E;

Under this discipline all commands (except the last in a CASE statement)
end with a semicolon, which you can think of as part of the command.  The
command to do nothing is just the semicolon by itself.  All the component
commands of a composite command are indented more than the first and last
lines; by looking straight down from the first line, you find the matching
last line, starting with END.

Comments are written before the command at the same level of indentation as
its first line.  Every line ends with either a semicolon, or the word BEGIN.

The system is flexible; when additions or deletions are made, you don't have
to change other lines.
APPENDIX J.  FILE SPECIFICATIONS [D4].
 
The full specification of any file belonging to you is of the form
	filename.filetype.generation
Use up to six letters and digits for the filename, chosen to help you 
remember what the file is about.  Use standard filetypes such as SAI,
according to the rules in File Types, below.  The generation number will be
suppled automatically.

To refer to a file in someone else's directory  [D4.3.1] , precede the 
filename with his user name or directory name in angular brackets:
	<s.smith>hisfilename.filetype.gen
You will find useful files in the directory  <C.CS10X>

There are other kinds of file names with special meanings [D4.2].

File Types [D4.5]

SAI:	a SAIL program
OUT:	the output program from a program
DAT:	input data for a program
TXT:	text in English (or other human language)
TMP:	a file which you want deleted when you log out
HDR:	a header file for incorporation into programs
REL or BIN:   a file in machine language
KNT:	a file of execution counts, used by PROFIL
LST:	a listing file, to be deleted after printing [D8.1.4]
DOC:	documentation
CRF:	a cross reference file [D8.2.4]
CMD:	a file of TOPS-20 commands to happen automatically when you log in
	[D3.2.4, D3.2.5]
CTL:	a control file for a batch job [D3.4]
LOG:	a log file for a batch job [D3.4]
EXE:	an executable file in machine language [D8.1.5]
INI:	a file of standard options to be used with EDIT, PRINT, and SUBMIT
	[D7.4]
FOR:	FORTRAN program

Files you create will usually be of types SAI, OUT, DAT, TXT, REL, or HDR.
HOW TO TALK ABOUT SETS OF FILES:
 
If you want to delete, print, list, see directory information about, etc.,
several files at once, you can name a subset of your files as follows.  The
asterisk,  * , stands for any file name, so  @%PRINT$ *.SAI%  will print
all your SAIL files.  It also stands for any file type, so
	@%DIRectory$ MYPROG.*.2%
will give you directory information about both  MYPROG.SAI.2  and
MYPROG.OUT.2 .  See [D4.8].

In most places where you would want to, you can give the names of a set of
files, separated by commas [D4.11], like
	@%PRINT$ MYPROG.SAI, RESULTS.OUT%
Giving a file name alone is like following it with  .*  [D4.5].

If you don't mention a generation number, the system provides one as follows:
When you create a file, the generation number used is one more than the 
highest existing generation of that file.  When you read a file (editing,
compiling, printing etc.) the highest generation is used.  When you delete
or undelete, all generations are used.

SOME USEFUL THINGS THAT CONTROL CHARACTERS DO (References are to DEC SYSTEM 20
OVERVIEW):
 
  C	(Twice) calls TOPS-20.
 
  I	Is the tab key.
 
  O	Stops, or restarts, output to the terminal, without stopping the 
	program that is running.
 
  Q	Looks at the next page of a multi-page terminal display.
 
  S  	Stops the terminal display from moving, until   Q   restrarts it.
	[D9.3.1]
 
  T	Gives load and usage statistics.  [D7.6.3]
 
  U	Cancels current line (then hit RETURN).
 
  W	Deletes last word typed.
 
  Z	Signs off when entering data from terminal.  In particular, used to
	end a message you are mailing.
 
ESC	Asks TOPS-20 to fill out the rest of a word for you if it can, and
	prompt for the next word.
RUBOUT	Deletes last character typed (so does BACKSPACE).
REPEAT	While held down, makes other keys repeeeeeeeeeat.
SPACE	Ends TOPS-20 operands.
?	Asks for a list of available choices you can type next.
!	Makes rest of the line a comment, ignored by TOPS-20.
APPENDIX K.  COMMON PROGRAMMING ERRORS.
 
(1)	Failure to have the same number of BEGINs and ENDs.  With too many
	ENDs there may be no error message.  With too few, the likely message
	is  FATAL END OF SOURCE FILE  at the end of the program.  Omission of
	BEGINs and ENDs is also likely to give rise to  UNDECLARED IDENTIFIER
	messages.  To control this problm, it is advisable to label BEGINs 
	and ENDs, at least for the blocks of substantial size (about ten
	lines).
(2)	Omission of the semicolon.  Symptoms:  INSERTING FORGOTTEN SEMICOLON
	or SYNTAX ERROR.
(3)	Putting some of the commands of a block before some of the
	declarations:
		BEGIN
		INTEGER N;
		N := READ;
		REAL ARRAY A[1:N];
		.
		.
                .
		END
(4)	Omission of semicolon after comments.  Symptom:  usually loss of the
	following command, often with no error message.
(5)	Including spaces in combinations like  := , <= , SOURCE!FILE ,
	SETFORMAT .
(6)	Omitting closing quote marks.  Symptoms: many.
	Example:  PRINT("X =,X,"Y =",Y);
(7)	Typing more than 80 characters per line.  (This goes to a new line on
	the terminal, but continues on the same line on paper listings of the
	program, eventually spilling some of the program off the right edge of
	the paper.)
(8)	Forgetting to provide for spacing in output.  Trying to print more
	than 132 characters per line, and more than 60 lines per page.
(9)	Running constants and identifiers together:  PRINT(10000DIV X) .
(10)	Omitting multiply signs:  PRINT((A+B)(C-D),B**2-4AC) .
	Symptom:  UNDECLARED VARIABLE.
(11)	Using unary minus after an operator without parenthesizing:  A**-B .
(12)	Failure to initialize variables used in an iteration.  Failure to
	initialize them often enough (at the right level of iteration).
(13)	Using an integer variable to hold a quantity which may logically be
	a non-integer:
		INTEGER C;
		C := SQRT(A**2 + B**2);
(14)	Failure to prompt for input; to check it for validity; to reread
	if invalid data are found; to echo all input in the output listing.
(15)	Including semicolon just before ELSE.
(16)	IF ... THEN C  ELSE C  ELSE C  .  Symptom:  SYNTAX ERROR.
		     1	     2	     3
(17)	Using  =  for  :=  :
		FOR I = 1 STEP 1 UNTIL 10 DO
			A[I] = 3*I-1;
(18)	Using  :=  for  =  :
		IF A := B THEN PRINT("YES") ELSE PRINT("NO")
(19)	Patterns like  WHILE X > Y THEN X := X-1  ,  IF P = Q DO PRINT(R)  ,
	REPEAT X := X-1 WHILE X > Y .
(20)	Forgetting that many operations, such as MOD and DIV, assign their
	arguments to integer variables before doing their work.
(21)	Conditional expressions without alternative:  A := IF B THEN C+D .
(22)	Forgetting that, as data, upper and lower case letters are not
	considered equal.  Example:
		PRINT("DO YOU WANT ...");
		S := READLINE
		IF EQU(S,"YES") THEN ... ELSE ...
Examples follow, showing typical error messages resulting from common SAIL
syntax errors.
 
The following is a correct SAIL program:
 
	@TYPE (FILE) ROOK.SAI.3
	00100	COMMENT ROOK ON CHESSBOARD;
	00200	BEGIN "MAIN"
	00300	REQUIRE "<C.CS10X>INTRO.HDR" SOURCE!FILE;
	00400	INTEGER ROOKRO, ROOKCOL, ROW, COL;
	00500 
	00600   ROOKROW := READ;
	00700	
	00800	WHILE ROOKROW < 1 OR ROOKROW > 8 DO BEGIN
	00900		PRINT("TRY AGAIN");
	01000		ROOKROW := READ;
	01100	END;
	01200
	01300	PRINT("ROW =", ROOKROW);
	01400	ROOKCOL := READ;
	01500
	01600	WHILE ROOKCOL < 1 OR ROOKCOL > 8 DO BEGIN
	01700		PRINT("TRY AGAIN");
	01800		ROOKCOL := READ;
	01900	END;
	02000
	02100	PRINT("COLUMN =", ROOKCOL, NEWLINE);
	02200	COMMENT NOW PRINT BOARD;
	02300
	02400	FOR ROW UPTO 8 DO BEGIN "ROW"
	02500		FOR COL UPTO 8 DO BEGIN "COL"
	02600			IF ROW = ROOKROW THEN BEGIN "ONROW"
	02700				IF COL = ROOKCOL THEN BEGIN
	02800					PRINT("R");
	02900				END ELSE BEGIN
	03000					PRINT("*");
	03100				END
	03200			END "ONROW" ELSE BEGIN "OFFROW"
	03300				IF COL = ROOKCOL THEN BEGIN
	03400					PRINT("*");
	03500				END ELSE BEGIN
	03600					PRINT(".");
	03700				END;
	03800			END "OFFROW";
	03900		END "COL";
	04000	END "ROW";
	04100
	04200	END "MAIN";
We shall show the effects of making changes that leave the program
grammatically incorrect.

        (1)  Omitting an END, by deleting line 1100.  When we execute the
program, we see the following on the terminal.

	@EXECUTE (FROM) ROOK.SAI
	ROOK.SAI.4 1
		<C.CS10X>INTRO.HDR.1 1
			<SAIL>IOSAIL.HDR.21 1
	Fatal end of source file, scanning file.
	BEGIN MAIN  00200/1
	Last source-file macro:   UPTO   02500/1
	Current macro:  UPTO
 
	ROOK.SAI.4, PAGE 1
	04100

The first line after the EXECUTE is the file ROOK.SAI.4 which was compiled,
followed by a  1  to show that the compiler was on page 1 of that file.  The
next line shows that the introductory header was incorporated (by the
REQUIRE line).  The third shows that the IOSAIL package in turn was REQUIREd
by INTRO.  The fourth line is the error message; the compiler came to the end
of the ROOK.SAI file without having seen a complete program.  The next line
shows that the incomplete structure was the BEGIN, labeled MAIN, on line
200 of page 1 (the only page) of the program.  The next two lines are
irrelevant to this error.  They refer to the fact that line 2500 uses UPTO
as an abbreviation for  := 1 STEP 1 UNTIL   .

	(2)  Failing to declare the variable  COL ,
		00400   INTEGER ROOKROW, ROOKCOL, ROW;
and omitting a semicolon
		01700   PRINT("TRY AGAIN")
causes these error messages.

	INSERTING FORGOTTEN SEMI-COLON.
	ROOK.SAI.5, PAGE 1
	01800		 ROOKCOL
				 := READ;

	UNDECLARED IDENTIFIER:  COL
	ROOK.SAI.5, PAGE 1
	02500		FOR COL
				UPTO 8 DO BEGIN "COL"

	IMPOSSIBLE TYPE COERCION
	ROOK.SAI.5, PAGE 1
	02700		IF COL = ROOKCOL THEN
              				       BEGIN

	IMPOSSIBLE TYPE COERCION
	ROOK.SAI.5, PAGE 1
	03300		IF COL = ROOKCOL THEN 
					       BEGIN

	End of compilation.

The first error causes multiple error messages, all at places where COL is
used.  Notice that the errors were not discovered until the compiler had gone
a few symbols past COL in each case.  The missing semicolon was automatically
restored; this is not always possible.  The line  1800  is broken, continuing
on a lower line in the error message, after  ROOKCOL ; that is where the
compiler was working when it found the error.  Often, as in this case, the
actual error was on an earlier line.

	(3)  Inserting a space into a symbol
		01400	ROOKCOL : = READ
(should be  := ) and inserting a semicolon before ELSE
		02900	END; ELSE BEGIN
causes these error messages:

	SYNTAX ERROR.  CURRENT STATEMENT OR DECLARATION WILL BE FLUSHED.
	ROOK.SAI.6, PAGE 1
	01400		ROOKCOL :
				  = READ;

	STATEMENT FLUSHED.

	EXTRANEOUS "ELSE", WILL BE IGNORED
	ROOK.SA.6, PAGE 1
	02900			END ; ELSE
					   BEGIN

	End of compilation.

The first error was not diagnosed in detail.  SYNTAX ERROR is a catchall
phrase for numerous grammatical problems.  The error was discovered when the
compiler was looking at
		ROOKCOL :
,however, which brings the reader's attention to the source of the problem.
The second error was diagnosed as resulting from an extra ELSE, rather than
an extra semicolon.  As this indicates, you should not assume that error
messages tell you how to fix your program.  They merely provide evidence
about what the compiler couldn't interpret.

	(4)  Leaving out an essential space
		00800	WHILE ROOKROW < 1 OR ROOKROW > 8DO BEGIN
and using WHILE ... THEN instead of WHILE ... DO
		01600	WHILE ROOKCOL < 1 OR ROOKCOL > 8 THEN BEGIN
causes these error messages:

	ILLEGAL REAL CONSTANT
	ROOK.SAI.7, PAGE 1
	00800		WHILE ROOKROW < 1 OR ROOKROW > 8D
							 O BEGIN

	ILLEGAL CONSTANT
	ROOK.SAI.7, PAGE 1
	00800		WHILE ROOKROW < 1 OR ROOKROW > 8D
							 O BEGIN

	SYNTAX ERROR AT END OF EXPRESSION - WILL CHECK FOR PARENTHESES MISMATCH
	ROOK.SAI.7, PAGE 1
	00800		WHILE ROOKROW < 1 OR ROOKROW > 8DO
							   BEGIN
	BLOCK FOUND WHILE FLUSHING STATEMENT - WILL TRY TO PARSE IT.
	BLOCK END OKAY - FLUSH OF STATEMENT CONTINUES.
	STATEMENT FLUSHED.

	SYNTAX ERROR AT END OF EXPRESSION - WILL CHECK FOR PARENTHESES MISMATCH
	ROOK.SAI.7, PAGE 1
	01600		WHILE ROOKCOL < 1 OR ROOKCOL > 8 THEN
							      BEGIN

	BLOCK FOUND WHILE FLUSHING STATEMENT - WILL TRY TO PARSE IT.
	BLOCK END OKAY - FLUSH OF STATEMENT CONTINUES.
	STATEMENT FLUSHED.

	End of compilation.

The first shows that the compiler began to interpret  8  as a numerical
constant, then found the  D  in it and was confused.  The same confusion
caused the next three error messages.  You will often find that correcting
the first error in a program will make many error messages go away.  The
last two error messages reflect the second error.

	(5)  Putting a declaration after a command, by putting line 400 
(now line 650) after line 600.
		00600	ROOKROW := READ;
		00650	INTEGER ROOKROW, ROOKCOL, ROW, COL;
resulted in these error messages:

	UNDECLARED IDENTIFIER:  ROOKROW
	ROOK.SAI.9, PAGE 1
	00600		ROOKROW
			        := READ;

	CANNOT BEGIN A DECL OR STMNT LIKE THIS.
	(MOST LIKELY A DECL AFTER A STMNT)
	ROOK.SAI.9, PAGE 1
	00650		INTEGER
			        ROOKROW, ROOKCOL, ROW, COL;

	STATEMENT FLUSHED.

	IMPOSSIBLE TYPE COERCION
	ROOK.SAI.9, PAGE 1
	00300		WHILE ROOKROW < 1 OR
					     ROOKROW, ROOKCOL, ROW, COL;

	IMPOSSIBLE TYPE COERCION
	ROOK.SAI.9, PAGE 1
	00300		WHILE ROOKROW < 1 OR ROOKROW > 8 DO
							    BEGIN

	Can't PRINT this syntactic type
	ROOK.SAI.9, PAGE 1
	01300		PRINT("ROW =", ROOKROW)
					       ;

	DRYROT -- GETAD
	ROOK.SAI.9, PAGE 1
	01300		PRINT("ROW =", ROOKROW)
					       ;
The second message correctly diagnosed the problem.  The compiler soon got
too confused to continue; this is what DRYROT means.

	(6)  Using  =  instead of  :=  as the assignment operator
		01800	ROOKCOL = READ;
and omitting a closing quote
		02100	PRINT("COLUMN =, ROOKCOL, NEWLINE);
resulted in these errors:

	SYNTAX ERROR.  CURRENT STATEMENT OR DECLARATION WILL BE FLUSHED.
	ROOK.SAI.11, PAGE 1
	01800		ROOKCOL =
				  READ;

	STATEMENT FLUSHED.

	SYNTAX.ERROR.  CURRENT STATEMENT OR DECLARATION WILL BE FLUSHED.
	ROOK.SAI.11, PAGE 1
	02400		FOR ROW UPTO 8 DO BEGIN "ROW
						    "

	Fatal end of source file, scanning file.
	BEGIN MAIN  00200/1
	Last source-file macro:  CLOSEALL  01400/1
	Current macro:  THRU

	ROOK.SAI.11, PAGE 1
	04200

The first message clearly reflects the first error.  The compiler thought that
the quoted string in line 02100 went on up to the first quote in line 02400,
where it was immdiately followed by ROW, as if one had written
		PRINT("XYZ" ROW...   .
From here on, most of the program (except for block labels) was thoght to be
long quoted strings, so the ENDs were missed.  At the end of the file, 
therefore, there were still incomplete blocks.  Omitting quote marks causes
very confusing error messages; check that every line includes an even number
of quote marks.
APPENDIX M.  POLYNOMIAL MULTIPLICATION.
 
Let  A[0] + A[1]X + A[2]X**2 + ... + A[DA]X**DA  and  B[0] + B[1]X + ...
+ B[DB]X**DB  be two polynomials.  To get the coefficients of the product
polynomial, form the product of every monomial  A[I]X**I  from  A , with
every monomial  B[J]X**J  from  B .  This contributes a term  A[I]B[J]X**(I+J)
to the product,  C , increasing  C[I+J]  by  A[I]B[J] :

	FOR K := 0 STEP 1 UNTIL DA + DB DO
		C[K] := 0;
	FOR I := 0 STEP 1 UNTIL DA DO
		FOR J := 0 STEP 1 UNTIL DB DO
			C[I+J] := C[I+J] + A[I] * B[J]
	FOR K := 0 STEP 1 UNTIL DA + DB DO
		PRINT(C[K],NEWLINE)

This organization of the nested iteration is much easier than trying to 
compute each coefficient of  C  completely before going on to the next.
If you try to do so, you find that the program must consist of about
three separate nested iterations.

Suppose you want to raise a polynomial  A  to a power -- perhaps the fifth.
Schematically, one way to do it is:
	Put  A*A  in  B
	Put  A*B  in  C    (i.e.,  A**3)
	Put  A*C  in  D    (i.e.,  A**4)
	Put  A*D  in  E    (i.e.,  A**5)
This entails writing out the nested loops for the multiplication four times.

It is much simpler to use another level of iteration:  Start  B  off as the
polynomial  1 , and let the outer iteration successively change its value
to  A , A**2 , A**3 , A**4 , A**5 .  This is done, schematically, by:

	Put  1  in  B ;
	FOR I := 1 STEP 1 UNTIL 5 DO
		BEGIN
		COMMENT ASSUME B IS A**(I-1)
		Put  A*B  in  C ;
		Put  C  in  B 
		COMMENT NOW B IS A**I
		END

Writing the program out in detail,
	BEGIN "MAIN"
	REAL ARRAY A[0:10], B, C[0:50];
	INTEGER DA, DB, DC, POWER;
	COMMENT INPUT POLYNOMIAL A;
	DA := READ;
	FOR I := 0 STEP 1 UNTIL DA DO
		A[I] := READ;
	COMMENT PUT 1 IN B;
	DB := 0;
	B[0] := 1;
	FOR POWER := 0 STEP 1 UNTIL 4 DO
		BEGIN "POW"
		DB := POWER * DA;
		DC := DA + DB;
		FOR K := 0 STEP 1 UNTIL DC DO C[K] := 0;
		FOR I := 0 STEP 1 UNTIL DA DO
			FOR J:= 0 STEP 1 UNTIL DB DO
				C[I+J] := C[I+J] + A[I] * B[J];
		COMMENT AB IS IN C;
		FOR K := 0 STEP 1 UNTIL DC DO
			B[K] := C[K]
		END "POW"
	END "MAIN"

The key to the above program is leaving the result of each iteration
(originally in array  C ) in the place (array  B ) where the input is 
expected for the next iteration.  Generally, if you want to iterate a
series of computations, you must match up its output with its own input.
[Into SAIL notes, insert before CONVERSION OF STRINGS TO NUMBERS.]
EXAMPLES OF STRING MANIPULATION IN SAIL.
 
In the following procedure bodies, it is assumed that the input is in string
variable  S , and that string variable  T  and integer (or string) variable  C
are available for use, along with integer variable  L .

(1)	To change the letters in a string to upper case:
		BEGIN
		INTEGER D;
		T := NULL;
		D := "A"-"a";
		WHILE S NEQ NULL DO
			BEGIN "MOVE"
			C := LOP(S);
			IF "a" <= C <= "z" THEN C := C+D;
			T := T & C
			END "MOVE"
		END

(2)	To change every occurrence of string  X  in  S  to a string  Y :
		BEGIN
		T := NULL;
		L := LENGTH(X);
		WHILE LENGTH(S) >= L DO
			BEGIN "MOVE"
			IF EQU(S[1 FOR L],X) THEN
				BEGIN
				T := T & Y;
				S := S[L+1 TO INF]
				END
			ELSE T := T  & LOP(S)
			END "MOVE"
		RETURN(T & S)
		END

(3)	To test whether  S  is alphabetically earlier than  T  (returning
	TRUE also if they are equal, and assuming that letters are all in
	upper case):
		BEGIN
		WHILE S NEQ NULL OR T NEQ NULL DO
			BEGIN
			IF S = NULL THEN S := SPACE
			ELSE IF T = NULL THEN T := SPACE;
			IF S < T THEN RETURN (TRUE);
			IF LOP(T) < LOP(S) THEN RETURN (FALSE)
			END;
		RETURN (TRUE)
		END
(4)	To remove initial, final, and multiple spaces from a string  S :
		BEGIN
		T := NULL;
		WHILE S = SPACE DO C := LOP(S);
		WHILE S NEQ NULL DO
			IF EQU(S[1 TO 2],"bb") THEN LOP(S)
			ELSE T := T & LOP(S);
		IF T[INF FOR 1] = SPACE
		THEN RETURN (T[1 TO INF - 1])
		ELSE RETURN (T)
		END

(5)	To insert  N  extra spaces in a string  S  as uniformly as possible
	at the ends of words in order to expand the string.  If  S  ends with
	a word, no spaces are inserted after it.
		BEGIN
		T := NULL;
		WHILE N NEQ 0 DO
			BEGIN "SHIFTCHAR"
			IF S = NULL THEN
				BEGIN
				S := T;
				T := NULL
				END;
			C := LOP(S);
			T := T & C;
			IF C NEQ SPACE AND S = SPACE THEN
				BEGIN "ADD SPACE"
				T := T & SPACE;
				N := N-1
				END "ADD SPACE"
			END "SHIFTCHAR"
		END

(6)	To remove from  S , and return, the longest initial group of words of
	not more than 50 characters.  
		BEGIN
		IF LENGTH(S) <= 50 THEN
			BEGIN
			T := S;
			S := NULL;
			RETURN (T)
			END;
		N := 50;
		WHILE S[N FOR 1] = SPACE OR S[N+1 FOR 1] NEQ SPACE DO
			N := N-1;
		T := S[1 TO N];
		S := S[N+1 TO INF];
		RETURN (T)
		END
RANDOM NUMBERS.
 
SAIL contains a random number generator, which, on successive uses, gives a
series of numbers uniformly distributed between  0  and  1 .  While the
numbers are not truly random, they satisfy most statistical tests for
randomness, and can be used to generate dice rolls, coin flips, and other
random events which might be wanted in a computer program.

The random number generator is ordinarily used once with a non-zero integer
argument, called the %seed%, to initialize it; the seed determines which
sequence of numbers it will generate.  Thereafter it is used only with a zero
argument, each usage giving a new random number.  To use the random number,
evaluate the expression  RAN(e) , for any integer expression  e ; 
X := RAN(314159)  initializes the generator with  314159  as the seed, and
also puts a random number into  X ; thereafter,  X := RAN(0)  gives new
random numbers.

From the values of  RAN(0) , most of the random numbers we might need can
be obtained.  To get a random roll of the dice, we first multiply  RAN(0)
by  6  (now it is a random number from  0  to  6 ), add  1  (now it is from
1  to  7 ), and take the integer part by assignment to an integer variable,
thus:
	INTEGER DICE
	.
	.
	.
	X := RAN(71828)
	.
	.
	.
	DICE := 6 * RAN(0) + 1
	.
	.
	.

For a random decimal digit  (0  through  9) , it would be 
	
	INTEGER DIGIT;
	DIGIT := 10 * RAN(0)

For a number uniformly distributed between  -3  and  17 ,
 
	REAL X;
	X := 20 * RAN(0) - 3

For a number with a triangular distribution, use the smaller of two random
numbers:
	X := RAN(0) MIN RAN(0)
or square a random number:
	X := RAN(0) ** 2
For a normally distributed number (this distribution is the bell-shaped
curve beloved of statisticians), add twenty four random numbers obtained
from RAN.  The resulting number,  S , has mean  12 , variance  4 .  To get
a number with mean  M  and variance  V , use
	(S-12) * .5 + M

Here is how RAN works.  It uses one integer variable, which we will call  R .
If, in the expression  RAN(X) ,  X  is not zero,  R  is initialized by
assigning  X  to it.  Then  R  is multiplied by a certain constant, which
we will call  K , and the low order  35  (?) bits are taken as the new value
of  R .  If  K  has been well chosen, the successive values of  R  behave
				        35                 -35
like randomly selected integers in  (0,2  -1) .  Now  R * 2     is  returned
as the value of  RAN(X) ; this number lies in the range  0 <= RAN(X) < 1 .

Example:  to generate and print a random hand of 5 cards.
	INTEGER SEED, I, CARD
	INTEGER ARRAY USED[0:52];
	SEED := 0;
	WHILE SEED = 0 DO
	BEGIN
	PRINT ("TYPE IN NON-ZERO SEED FOR RANDOM NUMBER GENERATOR");
	SEED := READ
	END;
	X := RAN(SEED);
	FOR I UPTO 52 DO
        		USED[I-1] := 0;
		USED[52] := 1
	FOR I UPTO 5 DO
		BEGIN
        	CARD := 52;
		WHILE USED[CARD] = 1 DO
			CARD := RAN(0) * 52;
		USED[CARD] := 1;
		PRINT ("A 2 3 4 5 6 7 8 9 10 J Q K "[(CARD REM 13)*2+1 FOR 2],
			"OF", "CLUBS   DIAMONDS HEARTS  SPADES  "
			[(CARD DIV 4)*8 + 1 FOR 8])
		END

The ideas of the above program are:  A nonzero seed is obtained from the user.
The array  USED  keeps track (by 1's) of which cards (code numbers  0  to  51 )
have been used.  A card code of  52  is an initialization convention to show
that no card has been picked yet.  The first 13 card codes (0 to 12) are 
clubs, the next 13 are diamonds, etc;  CARD DIV 13  gives the suit number
(0 to 3).  Card codes which are multiples of 13 are aces; codes one greater
are 2's, etc.   CARD REM 13  gives the rank number (0 to 12).